package fcnet

import (
	"encoding/json"
	"errors"
	"io"
	"net"

	"gitee.com/fcsvr/fc/fclog"
)

type Conn struct {
	ConnID       int
	IsClosed     bool
	ExitBuffChan chan bool
	MsgChan      chan []byte

	*net.TCPConn
}

func NewConn(conn *net.TCPConn, connid int) *Conn {
	c := &Conn{
		TCPConn:      conn,
		ConnID:       connid,
		IsClosed:     false,
		ExitBuffChan: make(chan bool, 1),
		MsgChan:      make(chan []byte, SvrMgr.ConfigSvr().MaxChanLen),
	}
	return c
}

func (this *Conn) DoInitHandle() {
	h := &Req{
		Conn: this,
	}
	SvrMgr.MsgHandle().DoInitHandle(h)
}

func (this *Conn) StartReader() {
	//fclog.Info("Conn StartReader")

	for {
		msg := NewMsgNull()

		//先读消息头
		fclog.Info("Conn Start read from conn")
		msgHead := make([]byte, msg.GetMsgHeadLen())
		n, err := io.ReadFull(this.TCPConn, msgHead)
		if err != nil {
			fclog.Error("Conn id = %d read msg head num = %d, err = %v", this.ConnID, n, err)
			//this.ExitBuffChan <- true
			break
		}

		//把消息头解析出来，得到消息体长度
		err = msg.UnPack(msgHead)
		if err != nil {
			fclog.Error("Conn id = %d unpack msg head err = %v", this.ConnID, err)
			//this.ExitBuffChan <- true
			break
		}

		//取出消息体长度
		msgBodyLen := msg.GetMsgBodyLen()
		if msgBodyLen <= 0 {
			fclog.Error("Conn id = %d read msg bodylen err, len = %d", this.ConnID, msgBodyLen)
			//this.ExitBuffChan <- true
			break
		}

		//再读出消息体
		msgBody := make([]byte, msgBodyLen)
		_, err = io.ReadFull(this.TCPConn, msgBody)
		if err != nil {
			fclog.Error("Conn id = %d read msg body err, len = %v", this.ConnID, err)
			//this.ExitBuffChan <- true
			break
		}
		msg.SetMsgBody(msgBody)

		//根据消息id，进行相应的函数处理
		fclog.Info("Conn read msg id = %d, len = %d, msg = %s", msg.GetMsgID(), msg.GetMsgBodyLen(), string(msgBody))
		req := NewReq(this, msg)
		SvrMgr.MsgHandle().DoMsgHandle(req)
		/*
			if SvrMgr.ConfigSvr().WorkerPoolLen <= 0 {
				//go SvrMgr.MsgHandle().DoMsgHandle(req)
				SvrMgr.MsgHandle().DoMsgHandle(req)
			} else {
				SvrMgr.MsgHandle().SendMsgToWorker(req)
			}
		*/
	}

	this.Stop()
	SvrMgr.Sync().ConnWp().Done()
}

func (this *Conn) SendMsg(id uint32, msgbody any) error {
	if this.IsClosed == true {
		fclog.Error("Conn id = %d SendMsg err, conn is closesd", this.GetConnID())
		return errors.New("Conn send msg err = conn is closed")
	}

	bodyBuf, _ := json.Marshal(msgbody)

	msg := NewMsg(id, bodyBuf)
	buff, err := msg.DoPack()
	if err != nil {
		fclog.Error("Conn id = %d, SendMsg err = %v", this.GetConnID(), err)
		return err
	}
	this.SendDataToChan(buff)

	fclog.Debug("Conn SendMsg id = %d, msg = %+v, buff = %+v", id, msg, buff)
	return nil
}

func (this *Conn) SendDataToChan(data []byte) error {
	if this.IsClosed == true {
		fclog.Error("Conn id = %d is closesd", this.GetConnID())
		return errors.New("Conn send data err, conn is closed")
	}

	this.MsgChan <- data
	return nil
}

func (this *Conn) StartWriter() {
	//fclog.Info("Conn StartWriter")
	for {
		select {
		case data, ok := <-this.MsgChan:
			if !ok {
				fclog.Error("Conn id = %d, StartWriter msg from chan err = %v", this.ConnID, ok)
				goto exitWriter
			}
			if _, err := this.TCPConn.Write(data); err != nil {
				fclog.Error("Conn id = %d, StartWriter write msg err = %v", this.ConnID, err)
				goto exitWriter
			}
		case <-this.ExitBuffChan:
			goto exitWriter
		}
	}

exitWriter:
	SvrMgr.Sync().ConnWp().Done()
}

func (this *Conn) Start() {
	this.DoInitHandle()

	SvrMgr.Sync().ConnWp().Add(1)
	go this.StartReader()
	SvrMgr.Sync().ConnWp().Add(1)
	go this.StartWriter()

	/*
		for {
			select {
			case <-this.ExitBuffChan:
				return
			}
		}
	*/
}

func (this *Conn) Stop() {
	fclog.Debug("Conn Stop")
	if this.IsClosed == true {
		return
	}

	this.IsClosed = true

	this.TCPConn.Close()

	this.ExitBuffChan <- true
	SvrMgr.Conn().DelConn(this.GetConnID())

	close(this.ExitBuffChan)
	fclog.Info("Conn id = %d Stop MsgChan closed, All conn num = %d", this.ConnID, SvrMgr.Conn().Len())
	close(this.MsgChan)
}

func (this *Conn) GetConnID() int {
	return this.ConnID
}

func (this *Conn) RemoteAddr() net.Addr {
	return this.TCPConn.RemoteAddr()
}

func (this *Conn) GetTCPConn() *net.TCPConn {
	return this.TCPConn
}
